// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package base

import (
	"flag"
	"fmt"
	"runtime"
	"strings"

	"cmd/go/internal/cfg"
)

var goflags []string // 缓存的$GOFLAGS列表；可以是-x或--x form 

// GOFLAGS从$GOFLAGS返回标志。
// 可以假设列表中每个标志包含一个字符串，
// 每个字符串以-name或--name开头。
func GOFLAGS() []string {
	InitGOFLAGS()
	return goflags
}

// InitGOFLAGS从$goflags初始化goflags列表。
// 如果goflags已经初始化，它将不执行任何操作。
func InitGOFLAGS() {
	if goflags != nil { // 已初始化
		return
	}

	goflags = strings.Fields(cfg.Getenv("GOFLAGS"))
	if len(goflags) == 0 {
		// 无事可做；避免在以后的InitGOFLAGS调用
		goflags = []string{}
		return
	}

	// 忽略go env和go bug中的坏标志，因为
	// 人们在调试
	// 时会用到坏标志，这是一个问题，也许他们在调试GOFLAGS。
	// （如果成功，两者都将显示GOFLAGS设置。）
	hideErrors := cfg.CmdName == "env" || cfg.CmdName == "bug"

	// 字符串返回的每个单词。字段必须是它自己的标志。
	// 要设置标志参数，请使用-x=value而不是-x value。
	// 对于布尔标志，-x可以，而不是-x=true。
	for _, f := range goflags {
		// 检查每个标志是否看起来像-x--x-x=value或--x=value。
		if !strings.HasPrefix(f, "-") || f == "-" || f == "--" || strings.HasPrefix(f, "---") || strings.HasPrefix(f, "-=") || strings.HasPrefix(f, "--=") {
			if hideErrors {
				continue
			}
			Fatalf("go: parsing $GOFLAGS: non-flag %q", f)
		}

		name := f[1:]
		if name[0] == '-' {
			name = name[1:]
		}
		if i := strings.Index(name, "="); i >= 0 {
			name = name[:i]
		}
		if !hasFlag(Go, name) {
			if hideErrors {
				continue
			}
			Fatalf("go: parsing $GOFLAGS: unknown flag -%s", name)
		}
	}
}

// boolFlag是flag的可选接口。标志包已知的值。
// （不清楚为什么package flag不导出此接口。）
type boolFlag interface {
	flag.Value
	IsBoolFlag() bool
}

// SetFromGOFLAGS使用$GOFLAGS中的设置设置给定标志集中的标志。
func SetFromGOFLAGS(flags *flag.FlagSet) {
	InitGOFLAGS()

	// 此循环类似于flag。解析，只是它忽略了goflags中的未知标志，所以设置goflags=-ldflags=-w 
	// 不会中断没有-ldflags的命令。
	// 它还调整输出，以明确报告的问题来自$GOFLAGS。
	where := "$GOFLAGS"
	if runtime.GOOS == "windows" {
		where = "%GOFLAGS%"
	}
	for _, goflag := range goflags {
		name, value, hasValue := goflag, "", false
		// 忽略无效标志，如“=”或“=value”。
		// 如果未在InitGOFlags中报告，则表示我们不想报告。
		if i := strings.Index(goflag, "="); i == 0 {
			continue
		} else if i > 0 {
			name, value, hasValue = goflag[:i], goflag[i+1:], true
		}
		if strings.HasPrefix(name, "--") {
			name = name[1:]
		}
		f := flags.Lookup(name[1:])
		if f == nil {
			continue
		}

		// 使用标志。一致地设置（而不是f.Value.Set），以便后续
		// 调用标志。Visit将正确访问已设置的标志。

		if fb, ok := f.Value.(boolFlag); ok && fb.IsBoolFlag() {
			if hasValue {
				if err := flags.Set(f.Name, value); err != nil {
					fmt.Fprintf(flags.Output(), "go: invalid boolean value %q for flag %s (from %s): %v\n", value, name, where, err)
					flags.Usage()
				}
			} else {
				if err := flags.Set(f.Name, "true"); err != nil {
					fmt.Fprintf(flags.Output(), "go: invalid boolean flag %s (from %s): %v\n", name, where, err)
					flags.Usage()
				}
			}
		} else {
			if !hasValue {
				fmt.Fprintf(flags.Output(), "go: flag needs an argument: %s (from %s)\n", name, where)
				flags.Usage()
			}
			if err := flags.Set(f.Name, value); err != nil {
				fmt.Fprintf(flags.Output(), "go: invalid value %q for flag %s (from %s): %v\n", value, name, where, err)
				flags.Usage()
			}
		}
	}
}

// InGOFLAGS返回GOFLAGS是否包含给定的标志，例如“-mod”。
func InGOFLAGS(flag string) bool {
	for _, goflag := range GOFLAGS() {
		name := goflag
		if strings.HasPrefix(name, "--") {
			name = name[1:]
		}
		if i := strings.Index(name, "="); i >= 0 {
			name = name[:i]
		}
		if name == flag {
			return true
		}
	}
	return false
}
